package com.cloudeasy.provider.service.impl;

import com.cloudeasy.PublicUtil;
import com.cloudeasy.base.constant.GlobalConstant;
import com.cloudeasy.base.dto.LoginAuthDto;
import com.cloudeasy.base.enums.ErrorCodeEnum;
import com.cloudeasy.core.support.BaseService;
import com.cloudeasy.provider.mapper.UacMenuMapper;
import com.cloudeasy.provider.model.constant.MenuConstant;
import com.cloudeasy.provider.model.domain.UacMenu;
import com.cloudeasy.provider.model.domain.UacRoleMenu;
import com.cloudeasy.provider.model.dto.menu.UacMenuStatusDto;
import com.cloudeasy.provider.model.enums.UacMenuStatusEnum;
import com.cloudeasy.provider.model.exceptions.UacBizException;
import com.cloudeasy.provider.model.vo.MenuVo;
import com.cloudeasy.provider.model.vo.ViewMenuVo;
import com.cloudeasy.provider.service.UacActionService;
import com.cloudeasy.provider.service.UacMenuService;
import com.cloudeasy.provider.service.UacRoleMenuService;
import com.cloudeasy.provider.utils.TreeUtil;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
import org.modelmapper.ModelMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

/**
 * Create By 郭金宝 on 2019/1/14.<br>
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class UacMenuServiceImpl extends BaseService<UacMenu> implements UacMenuService {
    @Resource
    private UacMenuMapper uacMenuMapper;
    @Resource
    private UacRoleMenuService uacRoleMenuService;
    @Resource
    private UacActionService uacActionService;

    @Override
    @Transactional(readOnly = true, rollbackFor = Exception.class)
    public List<MenuVo> getMenuVoList(Long userId, Long applicationId) {
        // 1.查询该用户下所有的菜单列表
        List<MenuVo> menuVoList = Lists.newArrayList();
        List<UacMenu> menuList;
        Set<UacMenu> menuSet = Sets.newHashSet();
        // 如果是admin则返回所有的菜单
        if (userId == 1L) {
            // 1.1 查询该用户下所有的菜单列表
            UacMenu uacMenuQuery = new UacMenu();
            uacMenuQuery.setStatus(UacMenuStatusEnum.ENABLE.getType());
            uacMenuQuery.setApplicationId(applicationId);
            uacMenuQuery.setOrderBy(" level asc,number asc");
            menuList = uacMenuMapper.selectMenuList(uacMenuQuery);
        } else {
            // 1.2查询该用户下所有的菜单列表
            menuVoList = uacMenuMapper.findMenuVoListByUserId(userId);
            if (PublicUtil.isEmpty(menuVoList)) {
                return null;
            }
            Set<Long> ids = Sets.newHashSet();
            for (final MenuVo menuVo : menuVoList) {
                ids.add(menuVo.getId());
            }

            List<UacMenu> ownMenuList = this.getMenuList(ids);

            // 查出所有含有菜单的菜单信息
            UacMenu uacMenu = new UacMenu();
            uacMenu.setStatus(UacMenuStatusEnum.ENABLE.getType());
            List<UacMenu> allMenuList = this.selectMenuList(uacMenu);
            Map<Long, UacMenu> map = Maps.newHashMap();
            for (final UacMenu menu : allMenuList) {
                map.put(menu.getId(), menu);
            }

            for (final UacMenu menu : ownMenuList) {
                getPid(menuSet, menu, map);
            }
            menuList = new ArrayList<>(menuSet);
        }
        List<MenuVo> list = getMenuVo(menuList);
        if (PublicUtil.isNotEmpty(menuVoList)) {
            list.addAll(menuVoList);
        }
        // 2.递归成树
        return TreeUtil.getChildMenuVos(list, 0L);
    }

    private void getPid(Set<UacMenu> menuSet, UacMenu menu, Map<Long, UacMenu> map) {
        UacMenu parent = map.get(menu.getPid());
        if (parent != null && parent.getId() != 0L) {
            menuSet.add(parent);
            getPid(menuSet, parent, map);
        }
    }

    private List<MenuVo> getMenuVo(List<UacMenu> list) {
        List<MenuVo> menuVoList = Lists.newArrayList();
        for (UacMenu uacMenu : list) {
            MenuVo menuVo = new MenuVo();
            BeanUtils.copyProperties(uacMenu, menuVo);
            menuVo.setUrl(uacMenu.getUrl());
            menuVo.setMenuName(uacMenu.getMenuName());
            menuVoList.add(menuVo);
        }
        return menuVoList;
    }

    @Override
    public int saveUacMenu(UacMenu menu, LoginAuthDto loginAuthDto) {
        Long pid = menu.getPid();
        menu.setUpdateInfo(loginAuthDto);
        UacMenu parentMenu = mapper.selectByPrimaryKey(pid);
        if (PublicUtil.isEmpty(parentMenu)) {
            throw new UacBizException(ErrorCodeEnum.UAC10013001, pid);
        }
        if (menu.isNew()) {

            UacMenu updateMenu = new UacMenu();
            menu.setLevel(parentMenu.getLevel() + 1);
            updateMenu.setLeaf(MenuConstant.MENU_LEAF_NO);
            updateMenu.setId(pid);
            Long menuId = super.generateId();
            menu.setId(menuId);
            int result = mapper.updateByPrimaryKeySelective(updateMenu);
            if (result < 1) {
                throw new UacBizException(ErrorCodeEnum.UAC10013002, menuId);
            }

            menu.setStatus(UacMenuStatusEnum.ENABLE.getType());
            menu.setCreatorId(loginAuthDto.getUserId());
            menu.setCreator(loginAuthDto.getUserName());
            menu.setLastOperatorId(loginAuthDto.getUserId());
            menu.setLastOperator(loginAuthDto.getUserName());
            // 新增的菜单是叶子节点
            menu.setLeaf(MenuConstant.MENU_LEAF_YES);
            return uacMenuMapper.insertSelective(menu);
        } else {
            return uacMenuMapper.updateByPrimaryKeySelective(menu);
        }
    }

    @Override
    public int deleteUacMenuById(Long id, LoginAuthDto loginAuthDto) {
        Preconditions.checkArgument(id != null, "菜单id不能为空");
        int result;
        // 获取当前菜单信息
        UacMenu uacMenuQuery = new UacMenu();
        uacMenuQuery.setId(id);
        uacMenuQuery = mapper.selectOne(uacMenuQuery);
        if (PublicUtil.isEmpty(uacMenuQuery)) {
            throw new UacBizException(ErrorCodeEnum.UAC10013003, id);
        }

        // 删除菜单与角色的关联关系
        UacRoleMenu uacRoleMenu = new UacRoleMenu();
        uacRoleMenu.setMenuId(id);
        uacRoleMenuService.delete(uacRoleMenu);


        // 删除菜单
        result = uacMenuMapper.deleteByPrimaryKey(id);
        if (result < 1) {
            logger.error("删除菜单失败 menuId={}", id);
            throw new UacBizException(ErrorCodeEnum.UAC10013008, id);
        }

        // 删除权限
        // TODO 应该先查询再删除
        uacActionService.deleteByMenuId(id);

        // 修改当前删除菜单的父菜单是否是叶子节点
        UacMenu updateParentUacMenu = new UacMenu();
        updateParentUacMenu.setId(uacMenuQuery.getPid());
        updateParentUacMenu.setLeaf(MenuConstant.MENU_LEAF_YES);
        // 是二三级
        if (Objects.equals(MenuConstant.MENU_LEVEL_TWO, uacMenuQuery.getLevel()) || Objects.equals(MenuConstant.MENU_LEVEL_THREE, uacMenuQuery.getLevel())) {
            // 查询是否是叶子节点
            int count = uacMenuMapper.selectMenuChildCountByPid(uacMenuQuery.getPid());
            if (count == 0) {
                uacMenuMapper.updateByPrimaryKeySelective(updateParentUacMenu);
            }
        }
        return result;
    }

    @Override
    public int enableMenuList(List<UacMenu> menuList, LoginAuthDto loginAuthDto) {
        UacMenu uacMenuUpdate = new UacMenu();
        int sum = 0;
        for (UacMenu menu : menuList) {
            uacMenuUpdate.setId(menu.getId());
            uacMenuUpdate.setVersion(menu.getVersion() + 1);
            uacMenuUpdate.setStatus(UacMenuStatusEnum.ENABLE.getType());
            uacMenuUpdate.setLastOperator(loginAuthDto.getLoginName());
            uacMenuUpdate.setLastOperatorId(loginAuthDto.getUserId());
            uacMenuUpdate.setUpdateTime(new Date());
            int result = mapper.updateByPrimaryKeySelective(uacMenuUpdate);
            if (result > 0) {
                sum += 1;
            } else {
                throw new UacBizException(ErrorCodeEnum.UAC10013004, menu.getId());
            }
        }
        return sum;
    }

    @Override
    @Transactional(readOnly = true, rollbackFor = Exception.class)
    public List<UacMenu> getAllParentMenuByMenuId(Long menuId) {
        UacMenu uacMenuQuery = new UacMenu();
        uacMenuQuery.setId(menuId);
        uacMenuQuery = mapper.selectOne(uacMenuQuery);
        List<UacMenu> uacMenuList = Lists.newArrayList();
        uacMenuList = buildParentNote(uacMenuList, uacMenuQuery);
        return uacMenuList;
    }

    @Override
    @Transactional(readOnly = true, rollbackFor = Exception.class)
    public List<UacMenu> getAllChildMenuByMenuId(Long menuId, String menuStatus) {
        UacMenu uacMenuQuery = new UacMenu();
        uacMenuQuery.setId(menuId);
        uacMenuQuery = mapper.selectOne(uacMenuQuery);
        List<UacMenu> uacMenuList = Lists.newArrayList();
        uacMenuList = buildNode(uacMenuList, uacMenuQuery, menuStatus);
        return uacMenuList;
    }

    @Override
    public int disableMenuList(List<UacMenu> menuList, LoginAuthDto loginAuthDto) {
        UacMenu uacMenuUpdate = new UacMenu();
        int sum = 0;
        for (UacMenu menu : menuList) {
            uacMenuUpdate.setId(menu.getId());
            uacMenuUpdate.setVersion(menu.getVersion() + 1);
            uacMenuUpdate.setStatus(UacMenuStatusEnum.DISABLE.getType());
            uacMenuUpdate.setLastOperator(loginAuthDto.getLoginName());
            uacMenuUpdate.setLastOperatorId(loginAuthDto.getUserId());
            uacMenuUpdate.setUpdateTime(new Date());
            int result = mapper.updateByPrimaryKeySelective(uacMenuUpdate);
            if (result > 0) {
                sum += 1;
            } else {
                throw new UacBizException(ErrorCodeEnum.UAC10013005, menu.getId());
            }
        }
        return sum;
    }

    @Override
    @Transactional(readOnly = true, rollbackFor = Exception.class)
    public List<UacMenu> selectMenuList(UacMenu uacMenu) {
        return uacMenuMapper.selectMenuList(uacMenu);
    }

    @Override
    @Transactional(readOnly = true, rollbackFor = Exception.class)
    public List<MenuVo> findAllMenuListByAuthResDto(LoginAuthDto authResDto) {
        List<MenuVo> voList = Lists.newArrayList();
        Preconditions.checkArgument(authResDto != null, "无权访问");

        if (!GlobalConstant.Sys.SUPER_MANAGER_LOGIN_NAME.equals(authResDto.getLoginName())) {
            voList = uacMenuMapper.findMenuVoListByUserId(authResDto.getUserId());
        } else {
            UacMenu uacMenuQuery = new UacMenu();
            // 查询启用的菜单
            uacMenuQuery.setStatus(UacMenuStatusEnum.ENABLE.getType());
            uacMenuQuery.setOrderBy(" level asc,number asc");
            List<UacMenu> list = uacMenuMapper.select(uacMenuQuery);
            for (UacMenu uacMenu : list) {
                MenuVo menuVo = new MenuVo();
                BeanUtils.copyProperties(uacMenu, menuVo);
                voList.add(menuVo);
            }
        }
        return voList;
    }

    @Override
    @Transactional(readOnly = true, rollbackFor = Exception.class)
    public ViewMenuVo getViewVoById(Long id) {
        Preconditions.checkArgument(id != null, "菜单ID不能为空");
        UacMenu menu = uacMenuMapper.selectByPrimaryKey(id);

        if (menu == null) {
            logger.error("找不到菜单信息id={}", id);
            throw new UacBizException(ErrorCodeEnum.UAC10013003, id);
        }

        // 获取父级菜单信息
        UacMenu parentMenu = uacMenuMapper.selectByPrimaryKey(menu.getPid());

        ModelMapper modelMapper = new ModelMapper();
        ViewMenuVo menuVo = modelMapper.map(menu, ViewMenuVo.class);

        if (parentMenu != null) {
            menuVo.setParentMenuName(parentMenu.getMenuName());
        }

        return menuVo;
    }

    @Override
    public void updateUacMenuStatusById(UacMenuStatusDto uacMenuStatusDto, LoginAuthDto loginAuthDto) {
        Long id = uacMenuStatusDto.getId();
        String status = uacMenuStatusDto.getStatus();
        Preconditions.checkArgument(id != null, "菜单ID不能为空");
        Preconditions.checkArgument(StringUtils.isNotEmpty(status), "菜单状态不能为空");

        UacMenu uacMenuQuery = this.selectByKey(id);
        if (MenuConstant.MENU_LEVEL_ROOT.equals(uacMenuQuery.getLevel())) {
            throw new UacBizException(ErrorCodeEnum.UAC10013007);
        }
        // 要处理的菜单集合
        List<UacMenu> menuList = Lists.newArrayList();

        int result;
        if (status.equals(UacMenuStatusEnum.DISABLE.getType())) {
            // 获取菜单以及子菜单
            menuList = this.getAllChildMenuByMenuId(id, UacMenuStatusEnum.ENABLE.getType());
            // 禁用菜单以及子菜单
            result = this.disableMenuList(menuList, loginAuthDto);
        } else {
            // 获取菜单、其子菜单以及父菜单
            UacMenu uacMenu = new UacMenu();
            uacMenu.setPid(id);
            result = this.selectCount(uacMenu);
            // 此菜单含有子菜单
            if (result > 0) {
                menuList = this.getAllChildMenuByMenuId(id, UacMenuStatusEnum.DISABLE.getType());
            }
            List<UacMenu> menuListTemp = this.getAllParentMenuByMenuId(id);
            for (UacMenu menu : menuListTemp) {
                if (!menuList.contains(menu)) {
                    menuList.add(menu);
                }
            }
            // 启用菜单、其子菜单以及父菜单
            result = this.enableMenuList(menuList, loginAuthDto);
        }
        if (result < 1) {
            throw new UacBizException(ErrorCodeEnum.UAC10013006, id);
        }
    }

    @Override
    @Transactional(readOnly = true, rollbackFor = Exception.class)
    public boolean checkMenuHasChildMenu(Long pid) {
        Preconditions.checkArgument(pid != null, "菜单pid不能为空");

        UacMenu uacMenu = new UacMenu();
        uacMenu.setStatus(UacMenuStatusEnum.ENABLE.getType());
        uacMenu.setPid(pid);

        return mapper.selectCount(uacMenu) > 0;
    }

    @Override
    public List<UacMenu> listMenuListByRoleId(Long roleId) {
        List<UacMenu> menuList = uacMenuMapper.listMenuListByRoleId(roleId);
        List<UacMenu> addMenuList = Lists.newArrayList();

        if (PublicUtil.isNotEmpty(menuList)) {
            for (UacMenu uacMenu : menuList) {
                getMenuList(addMenuList, uacMenu.getPid());
            }
        }
        menuList.addAll(addMenuList);
        return new ArrayList<>(new HashSet<>(menuList));
    }

    @Override
    public List<UacMenu> getMenuList(final Set<Long> menuIdList) {
        return uacMenuMapper.listMenu(menuIdList);
    }

    private List<UacMenu> getMenuList(List<UacMenu> uacMenuList, Long menuId) {
        UacMenu uacMenu = uacMenuMapper.selectByPrimaryKey(menuId);
        if (uacMenu != null) {
            Long pid = uacMenu.getPid();
            if (pid != null) {
                uacMenuList.add(uacMenu);
                getMenuList(uacMenuList, pid);
            }
        }
        return uacMenuList;
    }

    /**
     * 递归获取菜单的子菜单
     */
    private List<UacMenu> buildNode(List<UacMenu> uacMenuList, UacMenu uacMenu, String menuStatus) {
        List<UacMenu> uacMenuQueryList = mapper.select(uacMenu);
        UacMenu uacMenuQuery;
        for (UacMenu menu : uacMenuQueryList) {
            // 启用状态
            if (menuStatus.equals(menu.getStatus()) && !MenuConstant.MENU_LEVEL_ROOT.equals(menu.getLevel())) {
                uacMenuList.add(menu);
            }
            uacMenuQuery = new UacMenu();
            uacMenuQuery.setPid(menu.getId());
            buildNode(uacMenuList, uacMenuQuery, menuStatus);
        }
        return uacMenuList;
    }

    /**
     * 递归获取菜单的父菜单
     */
    private List<UacMenu> buildParentNote(List<UacMenu> uacMenuList, UacMenu uacMenu) {
        List<UacMenu> uacMenuQueryList = mapper.select(uacMenu);
        UacMenu uacMenuQuery;
        for (UacMenu menu : uacMenuQueryList) {
            if (UacMenuStatusEnum.DISABLE.getType().equals(menu.getStatus()) && !MenuConstant.MENU_LEVEL_ROOT.equals(menu.getLevel())) {
                // 禁用状态
                uacMenuList.add(menu);
            }
            uacMenuQuery = new UacMenu();
            uacMenuQuery.setId(menu.getPid());
            buildParentNote(uacMenuList, uacMenuQuery);
        }
        return uacMenuList;
    }
}